//+------------------------------------------------------------------+
//|                                          GoldSignalGenerator.mqh |
//|                                    Copyright 2024, Precision EA  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Precision EA"
#property strict

#include "Logger.mqh"

class IndicatorManager {
private:
    string m_symbol;
    ENUM_TIMEFRAMES m_tf;
    
    struct IndicatorHandle {
        int handle;
        string name;
        datetime last_update;
    };
    
    IndicatorHandle m_handles[20];
    int m_handle_count;
    
    int GetOrCreateHandle(string name, int period1 = 0, int period2 = 0, int period3 = 0) {
        // Check if handle exists and is still valid
        for(int i = 0; i < m_handle_count; i++) {
            if(m_handles[i].name == name) {
                if(m_handles[i].handle != INVALID_HANDLE) {
                    return m_handles[i].handle;
                } else {
                    // Remove invalid handle
                    for(int j = i; j < m_handle_count - 1; j++) {
                        m_handles[j] = m_handles[j + 1];
                    }
                    m_handle_count--;
                    break;
                }
            }
        }
        
        // Create new handle
        int handle = INVALID_HANDLE;
        
        if(name == "EMA") {
            handle = iMA(m_symbol, m_tf, period1, 0, MODE_EMA, PRICE_CLOSE);
        } else if(name == "ATR") {
            handle = iATR(m_symbol, m_tf, period1);
        } else if(name == "RSI") {
            handle = iRSI(m_symbol, m_tf, period1, PRICE_CLOSE);
        } else if(name == "MACD") {
            handle = iMACD(m_symbol, m_tf, period1, period2, period3, PRICE_CLOSE);
        } else if(name == "Stochastic") {
            handle = iStochastic(m_symbol, m_tf, period1, period2, period3, MODE_SMA, STO_LOWHIGH);
        } else if(name == "ADX") {
            handle = iADX(m_symbol, m_tf, period1);
        }
        
        if(handle != INVALID_HANDLE) {
            m_handles[m_handle_count].handle = handle;
            m_handles[m_handle_count].name = name;
            m_handles[m_handle_count].last_update = TimeCurrent();
            m_handle_count++;
            
            Logger::Debug(StringFormat("Created %s handle: %d", name, handle), "IndicatorManager");
        } else {
            Logger::Error(StringFormat("Failed to create %s indicator", name), "IndicatorManager");
        }
        
        return handle;
    }
    
    bool CopyBufferSafe(int handle, int buffer_num, int start_pos, int count, double &buffer[]) {
        if(handle == INVALID_HANDLE) {
            Logger::Error("Invalid handle", "IndicatorManager");
            return false;
        }
        
        int copied = CopyBuffer(handle, buffer_num, start_pos, count, buffer);
        if(copied <= 0) {
            int error = GetLastError();
            Logger::Error(StringFormat("Failed to copy buffer: %d", error), "IndicatorManager");
            return false;
        }
        
        // Validate buffer values
        for(int i = 0; i < copied; i++) {
            if(!MathIsValidNumber(buffer[i])) {
                Logger::Error("Invalid value in buffer", "IndicatorManager");
                return false;
            }
        }
        
        return true;
    }

public:
    IndicatorManager(string symbol, ENUM_TIMEFRAMES tf) : 
        m_symbol(symbol), m_tf(tf), m_handle_count(0) {
        ArrayInitialize(m_handles, 0);
    }
    
    ~IndicatorManager() {
        for(int i = 0; i < m_handle_count; i++) {
            if(m_handles[i].handle != INVALID_HANDLE) {
                IndicatorRelease(m_handles[i].handle);
            }
        }
        Logger::Info("IndicatorManager destroyed", "IndicatorManager");
    }
    
    double GetEMA(int period, int shift = 0) {
        int handle = GetOrCreateHandle("EMA", period);
        double buffer[1];
        if(CopyBufferSafe(handle, 0, shift, 1, buffer)) {
            return buffer[0];
        }
        return 0.0;
    }
    
    double GetATR(int period = 14, int shift = 0) {
        int handle = GetOrCreateHandle("ATR", period);
        double buffer[1];
        if(CopyBufferSafe(handle, 0, shift, 1, buffer)) {
            return buffer[0];
        }
        return 0.0;
    }
    
    double GetRSI(int period = 14, int shift = 0) {
        int handle = GetOrCreateHandle("RSI", period);
        double buffer[1];
        if(CopyBufferSafe(handle, 0, shift, 1, buffer)) {
            return MathMax(0, MathMin(100, buffer[0]));
        }
        return 50.0;
    }
    
    void GetMACD(int fast = 12, int slow = 26, int signal = 9, int shift = 0, 
                 double &main, double &signal_line) {
        int handle = GetOrCreateHandle("MACD", fast, slow, signal);
        double main_buf[1], signal_buf[1];
        
        if(CopyBufferSafe(handle, 0, shift, 1, main_buf) &&
           CopyBufferSafe(handle, 1, shift, 1, signal_buf)) {
            main = main_buf[0];
            signal_line = signal_buf[0];
        } else {
            main = 0;
            signal_line = 0;
        }
    }
    
    void CleanupOldIndicators(datetime threshold) {
        for(int i = m_handle_count - 1; i >= 0; i--) {
            if(m_handles[i].last_update < threshold) {
                if(m_handles[i].handle != INVALID_HANDLE) {
                    IndicatorRelease(m_handles[i].handle);
                }
                
                // Remove from array
                for(int j = i; j < m_handle_count - 1; j++) {
                    m_handles[j] = m_handles[j + 1];
                }
                m_handle_count--;
            }
        }
    }
};

class GoldSignalGenerator {
private:
    string m_symbol;
    IndicatorManager *m_h1;
    IndicatorManager *m_h4;
    IndicatorManager *m_m15;
    
    // Configuration
    struct Config {
        int ema_fast;
        int ema_slow;
        int ema_trend;
        int atr_period;
        int rsi_period;
        double min_adx;
        double rsi_overbought;
        double rsi_oversold;
        double min_confluence;
        double atr_multiplier_sl;
        double atr_multiplier_tp;
    } m_config;
    
    // State
    datetime m_last_signal_time;
    double m_last_atr;
    double m_last_price;
    
    bool InitializeIndicators() {
        m_h1 = new IndicatorManager(m_symbol, PERIOD_H1);
        m_h4 = new IndicatorManager(m_symbol, PERIOD_H4);
        m_m15 = new IndicatorManager(m_symbol, PERIOD_M15);
        
        // Test indicators
        double test_atr = m_h1->GetATR(m_config.atr_period);
        if(test_atr <= 0) {
            Logger::Error("Failed to initialize ATR indicator", "SignalGenerator");
            return false;
        }
        
        Logger::Info("Indicators initialized", "SignalGenerator");
        return true;
    }
    
    bool IsValidTradingSession() {
        MqlDateTime now;
        TimeCurrent(now);
        
        // No trading on weekends
        if(now.day_of_week == 0 || now.day_of_week == 6) {
            return false;
        }
        
        // London session: 8:00-17:00 UTC+1
        // Convert to UTC for checking
        datetime utc = TimeCurrent();
        MqlDateTime utc_dt;
        TimeGMT(utc_dt);
        
        // Check London hours (7:00-16:00 UTC)
        return (utc_dt.hour >= 7 && utc_dt.hour < 16);
    }
    
    double CalculateConfluence(ENUM_ORDER_TYPE direction) {
        double score = 0.0;
        int factors = 0;
        
        // 1. Trend alignment (H4)
        double ema_fast_h4 = m_h4->GetEMA(m_config.ema_fast);
        double ema_slow_h4 = m_h4->GetEMA(m_config.ema_slow);
        
        if((direction == ORDER_TYPE_BUY && ema_fast_h4 > ema_slow_h4) ||
           (direction == ORDER_TYPE_SELL && ema_fast_h4 < ema_slow_h4)) {
            score += 0.3;
            factors++;
        }
        
        // 2. Momentum (RSI)
        double rsi = m_h1->GetRSI(m_config.rsi_period);
        if((direction == ORDER_TYPE_BUY && rsi > 30 && rsi < 70) ||
           (direction == ORDER_TYPE_SELL && rsi < 70 && rsi > 30)) {
            score += 0.2;
            factors++;
        }
        
        // 3. MACD signal
        double macd_main, macd_signal;
        m_h1->GetMACD(12, 26, 9, 0, macd_main, macd_signal);
        
        if((direction == ORDER_TYPE_BUY && macd_main > macd_signal) ||
           (direction == ORDER_TYPE_SELL && macd_main < macd_signal)) {
            score += 0.2;
            factors++;
        }
        
        // 4. Price position relative to EMA
        double ema_fast_h1 = m_h1->GetEMA(m_config.ema_fast);
        
        if((direction == ORDER_TYPE_BUY && m_last_price > ema_fast_h1) ||
           (direction == ORDER_TYPE_SELL && m_last_price < ema_fast_h1)) {
            score += 0.2;
            factors++;
        }
        
        // 5. Volatility filter (avoid low ATR)
        if(m_last_atr > 0 && m_last_atr/m_last_price > 0.001) {
            score += 0.1;
            factors++;
        }
        
        return (factors > 0) ? score / factors : 0.0;
    }
    
    void UpdateMarketData() {
        m_last_price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
        m_last_atr = m_h1->GetATR(m_config.atr_period);
        
        if(m_last_atr <= 0 || m_last_price <= 0) {
            Logger::Error("Invalid market data", "SignalGenerator");
        }
    }

public:
    GoldSignalGenerator(string symbol = "XAUUSD") : m_symbol(symbol), m_last_signal_time(0) {
        // Default configuration
        m_config.ema_fast = 21;
        m_config.ema_slow = 50;
        m_config.ema_trend = 200;
        m_config.atr_period = 14;
        m_config.rsi_period = 14;
        m_config.min_adx = 25.0;
        m_config.rsi_overbought = 70.0;
        m_config.rsi_oversold = 30.0;
        m_config.min_confluence = 0.6;
        m_config.atr_multiplier_sl = 1.5;
        m_config.atr_multiplier_tp = 2.0;
        
        if(!InitializeIndicators()) {
            Logger::Critical("Failed to initialize signal generator", "SignalGenerator");
        }
    }
    
    ~GoldSignalGenerator() {
        if(m_h1 != NULL) delete m_h1;
        if(m_h4 != NULL) delete m_h4;
        if(m_m15 != NULL) delete m_m15;
    }
    
    struct Signal {
        ENUM_ORDER_TYPE type;
        double entry_price;
        double stop_loss;
        double take_profit;
        double confidence;
        string reason;
        bool valid;
        datetime timestamp;
        
        Signal() : valid(false), confidence(0), timestamp(0) {}
    };
    
    Signal GenerateSignal() {
        Signal signal = {};
        signal.timestamp = TimeCurrent();
        
        // Check minimum time between signals (15 minutes)
        if(m_last_signal_time > 0 && 
           signal.timestamp - m_last_signal_time < 15 * 60) {
            signal.reason = "Minimum time between signals not reached";
            return signal;
        }
        
        // Update market data
        UpdateMarketData();
        
        // Check trading session
        if(!IsValidTradingSession()) {
            signal.reason = "Outside valid trading session";
            return signal;
        }
        
        // Calculate confluence for both directions
        double buy_confluence = CalculateConfluence(ORDER_TYPE_BUY);
        double sell_confluence = CalculateConfluence(ORDER_TYPE_SELL);
        
        // Determine direction with highest confluence
        if(buy_confluence > sell_confluence && buy_confluence >= m_config.min_confluence) {
            signal.type = ORDER_TYPE_BUY;
            signal.confidence = buy_confluence;
        } else if(sell_confluence > buy_confluence && sell_confluence >= m_config.min_confluence) {
            signal.type = ORDER_TYPE_SELL;
            signal.confidence = sell_confluence;
        } else {
            signal.reason = StringFormat("Insufficient confluence (Buy: %.2f, Sell: %.2f)", 
                buy_confluence, sell_confluence);
            return signal;
        }
        
        // Calculate price levels
        signal.entry_price = (signal.type == ORDER_TYPE_BUY) ? 
            SymbolInfoDouble(m_symbol, SYMBOL_ASK) : 
            SymbolInfoDouble(m_symbol, SYMBOL_BID);
        
        // Calculate SL based on ATR
        double atr_sl = m_last_atr * m_config.atr_multiplier_sl;
        double min_sl = 100 * SymbolInfoDouble(m_symbol, SYMBOL_POINT); // Minimum 100 points
        
        double sl_distance = MathMax(atr_sl, min_sl);
        
        if(signal.type == ORDER_TYPE_BUY) {
            signal.stop_loss = signal.entry_price - sl_distance;
            signal.take_profit = signal.entry_price + (sl_distance * m_config.atr_multiplier_tp);
        } else {
            signal.stop_loss = signal.entry_price + sl_distance;
            signal.take_profit = signal.entry_price - (sl_distance * m_config.atr_multiplier_tp);
        }
        
        // Validate price levels
        double point = SymbolInfoDouble(m_symbol, SYMBOL_POINT);
        double min_stop_level = SymbolInfoInteger(m_symbol, SYMBOL_TRADE_STOPS_LEVEL) * point;
        
        if(MathAbs(signal.entry_price - signal.stop_loss) < min_stop_level) {
            signal.reason = "Stop loss too close to entry";
            return signal;
        }
        
        // Check risk/reward ratio (minimum 1:1.5)
        double risk = MathAbs(signal.entry_price - signal.stop_loss);
        double reward = MathAbs(signal.entry_price - signal.take_profit);
        
        if(reward/risk < 1.5) {
            signal.take_profit = (signal.type == ORDER_TYPE_BUY) ? 
                signal.entry_price + (risk * 1.5) : 
                signal.entry_price - (risk * 1.5);
        }
        
        signal.valid = true;
        signal.reason = StringFormat("%s signal with %.0f%% confidence", 
            EnumToString(signal.type), signal.confidence * 100);
        
        m_last_signal_time = signal.timestamp;
        
        Logger::Info(signal.reason, "SignalGenerator");
        return signal;
    }
    
    void Cleanup() {
        datetime threshold = TimeCurrent() - 3600; // Cleanup indicators older than 1 hour
        if(m_h1 != NULL) m_h1->CleanupOldIndicators(threshold);
        if(m_h4 != NULL) m_h4->CleanupOldIndicators(threshold);
        if(m_m15 != NULL) m_m15->CleanupOldIndicators(threshold);
    }
    
    double GetCurrentATR() const { return m_last_atr; }
    double GetCurrentPrice() const { return m_last_price; }
};